package com.hetang.maijiayun.adaptertools.b2cinterface.paipai.client;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.hetang.maijiayun.adaptertools.b2cinterface.paipai.request.Request;
import com.hetang.maijiayun.adaptertools.b2cinterface.paipai.request.SellerSearchDealListRequest;
import com.hetang.maijiayun.adaptertools.b2cinterface.paipai.response.Response;
import com.hetang.maijiayun.adaptertools.b2cinterface.paipai.response.SellerSearchDealListResponse;
import com.hetang.maijiayun.adaptertools.utils.AdapterException;
import com.hetang.maijiayun.adaptertools.utils.AdapterExceptionErrorCode;
import com.hetang.maijiayun.adaptertools.utils.Base64Coder;
import com.hetang.maijiayun.adaptertools.utils.FileItem;
import com.hetang.maijiayun.adaptertools.utils.JsonUtil;
import com.hetang.maijiayun.adaptertools.utils.WebUtils;

public class PaipaiClient {
	
	private Map<String, String> params = new HashMap<String, String>();
	private Map<String, FileItem> fileParams = new HashMap<String, FileItem>();
	
	private final String CHARSET = "GBK";
	/**
	 * 如果使用json格式返回数据，请加入参数
	 * pureData=1
	 * 该参数只有format=json时才有意义。pureData=1时，
	 * json格式只会包含一个纯粹的json数据对象，
	 * 而不会包含其他js代码，如try...catch、回调函数等。
	 * 非paipai.com和qq.com的web页面引用时，pureData必定为1。
	 */
	private final String format = "json";
	private final String serverUrl;
	private String method="GET";
	
	private String appOAuthkey=null;
	private String appOAuthID;
	private String accessToken;
	private String uin;

	public PaipaiClient(String url,String appOAuthID,String appOAuthkey,String accessToken,String uin){
		this.serverUrl = url;
		this.appOAuthID = appOAuthID;
		this.appOAuthkey = appOAuthkey;
		this.accessToken = accessToken;
		this.uin = uin;
		params = new TreeMap<String, String>();
	}
	
	/**
	 * sessionKey 非必须
	 * @param <T>
	 * @param request
	 * @return
	 * @throws PaipaiException 
	 * @throws AdapterException 
	 */
	public <T extends Response> T execute(Request request,Class<T> clazz) throws PaipaiException, AdapterException {
		String url = serverUrl + request.getApiMethodName();
		String secret = appOAuthkey+"&";
	    if(fileParams!=null && fileParams.size()>0){
	    	method = "POST";
	    }
	    params.clear();
	    long timeStamp=System.currentTimeMillis();
	    String randomValue=""+(long)(Math.random()*100000000);
	    params.put("format", format);
	    params.put("charset", CHARSET);
	    params.put("timeStamp", timeStamp+"");
	    params.put("randomValue", randomValue);
		
	    params.put("appOAuthID", appOAuthID);
	    params.put("accessToken", accessToken);
	    params.put("uin", uin);
	    
	    params.putAll(request.getParams());
	    String sign = makeSign(method, secret, request.getApiMethodName(), params, CHARSET);		
	    params.put("sign", sign);
		
		int connectTimeout=3000;
		int readTimeout=5000;
		String retrunJsonStr = "";//
		try {
			if (fileParams != null && fileParams.size() > 0) {
				retrunJsonStr = WebUtils.doPost(url, params, fileParams, CHARSET, connectTimeout, readTimeout);
			} else if ("POST".equalsIgnoreCase(method)) {
				retrunJsonStr = WebUtils.doPost(url, params, CHARSET, connectTimeout, readTimeout);
			} else {
				retrunJsonStr = WebUtils.doGet(url, params, CHARSET);
			}
		} catch (IOException ie) {
			throw new AdapterException(AdapterExceptionErrorCode.B2C_ERROR_CODE, ie.getMessage());
		}
//		System.out.println(retrunJsonStr);
		return JsonUtil.getBean(retrunJsonStr, clazz);
	}
	
	/** 生成签名
     * @param method HTTP请求方法 "get" / "post"
     * @param url_path CGI名字, 
     * @param params URL请求参数
     * @param secret 密钥
     * @return 签名值
     * @throws OpensnsException 不支持指定编码以及不支持指定的加密方法时抛出异常。
     */
    private String makeSign(String method, String secret, String apiPath, Map<String, String> params, String charset) throws PaipaiException{
    	String sig ="";
        try{
            Mac mac = Mac.getInstance("HmacSHA1");
            SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(charset), mac.getAlgorithm());
            mac.init(secretKey);
            String mk = makeSource(method, apiPath, params, charset);
            byte[] hash = mac.doFinal(mk.getBytes(charset));
            sig = new String(Base64Coder.encode(hash));
//            sig = encodeUrl(sig);
        }catch(Exception e){
            throw new PaipaiException(PaipaiException.MAKE_SIGNATURE_ERROR, e);
        }
        return sig;
    }

    /** 
     * URL编码 (符合FRC1738规范)
     * @param input 待编码的字符串
     * @return 编码后的字符串
     * @throws PaipaiException 不支持指定编码时抛出异常。
     */
    private String encodeUrl(String input, String charset) throws PaipaiException{
        try{
            return URLEncoder.encode(input, charset).replace("+", "%20").replace("*", "%2A");
        }catch(UnsupportedEncodingException e){
            throw new PaipaiException(PaipaiException.MAKE_SIGNATURE_ERROR, e);
        }
    }

    /** 生成签名所需源串
     * @param method HTTP请求方法 "get" / "post"
     * @param url_path CGI名字, 
     * @param params URL请求参数
     * @return 签名所需源串
     */
    private String makeSource(String method, String url_path, Map<String, String> params, String charset) throws PaipaiException, UnsupportedEncodingException{
        Object[] keys = params.keySet().toArray();
        Arrays.sort(keys);  
        StringBuilder buffer = new StringBuilder(128);
        buffer.append(method.toUpperCase()).append("&").append(encodeUrl(url_path, charset)).append("&");
        StringBuilder buffer2= new StringBuilder();
        for(int i=0; i<keys.length; i++){  
            buffer2.append(keys[i]).append("=").append(params.get(keys[i]));
            if (i!=keys.length-1){
                buffer2.append("&");
            }
        }   
        buffer.append(encodeUrl(buffer2.toString(),charset));
        return buffer.toString();
    }

	public SellerSearchDealListResponse execute(SellerSearchDealListRequest req) throws PaipaiException, AdapterException {
		return execute(req,SellerSearchDealListResponse.class);
	}
}
